#include "driverlib.h"
#include "device.h"
#include "common.h"

static unsigned int gSecCounter0 = 0;
static unsigned int gSecCounter1 = 0;
static unsigned int gSecCounter2 = 0;

static unsigned int gIntNumCounter0 = 0;
static unsigned int gIntNumCounter1 = 0;
static unsigned int gIntNumCounter2 = 0;

__interrupt void cpuTimer0ISR(void)
{
    static int cnt0 = 0;

    ++gIntNumCounter0;
    if (++cnt0 == 100)
    {
        gSecCounter0++;
        cnt0 = 0;
    }

    CPUTimer_clearInterrupt(CPUTIMER0_BASE);
}

__interrupt void cpuTimer1ISR(void)
{
    static int cnt1 = 0;

    ++gIntNumCounter1;
    if (++cnt1 == 200)
    {
        gSecCounter1++;
        cnt1 = 0;
    }

    CPUTimer_clearInterrupt(CPUTIMER1_BASE);
}

__interrupt void cpuTimer2ISR(void)
{
    static int cnt2 = 0;

    ++gIntNumCounter2;
    if (++cnt2 == 1000)
    {
        gSecCounter2++;
        cnt2 = 0;
    }

    CPUTimer_clearInterrupt(CPUTIMER2_BASE);
}

static void config_start_all_timers()
{
    Interrupt_register(INT_TIMER0, cpuTimer0ISR);
    Interrupt_register(INT_TIMER1, cpuTimer1ISR);
    Interrupt_register(INT_TIMER2, cpuTimer2ISR);

    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CPU0_TIMER0);
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CPU0_TIMER1);
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CPU0_TIMER2);

    CPUTimer_setPreScaler(CPUTIMER0_BASE, 0);
    CPUTimer_setPeriod(CPUTIMER0_BASE, 10000);
    CPUTimer_enableInterrupt(CPUTIMER0_BASE);

    CPUTimer_setPreScaler(CPUTIMER1_BASE, 0);
    CPUTimer_setPeriod(CPUTIMER1_BASE, 5000);
    CPUTimer_enableInterrupt(CPUTIMER1_BASE);

    CPUTimer_setPreScaler(CPUTIMER2_BASE, 0);
    CPUTimer_setPeriod(CPUTIMER2_BASE, 1000);
    CPUTimer_enableInterrupt(CPUTIMER2_BASE);

    Interrupt_enable(INT_TIMER0);
    Interrupt_enable(INT_TIMER1);
    Interrupt_enable(INT_TIMER2);

    CPUTimer_startTimer(CPUTIMER0_BASE);
    CPUTimer_startTimer(CPUTIMER1_BASE);
    CPUTimer_startTimer(CPUTIMER2_BASE);
}

/* BEEBS st benchmark

   This version, copyright (C) 2014-2019 Embecosm Limited and University of
   Bristol

   Contributor James Pallister <james.pallister@bristol.ac.uk>
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>

   This file is part of Embench and was formerly part of the Bristol/Embecosm
   Embedded Benchmark Suite.

   SPDX-License-Identifier: GPL-3.0-or-later */

/* stats.c */

/* 2012/09/28, Jan Gustafsson <jan.gustafsson@mdh.se>
 * Changes:
 *  - time is only enabled if the POUT flag is set
 *  - st.c:30:1:  main () warning: type specifier missing, defaults to 'int':
 *    fixed
 */

/* 2011/10/18, Benedikt Huber <benedikt@vmars.tuwien.ac.at>
 * Changes:
 *  - Measurement and Printing the Results is only enabled if the POUT flag is
 *    set
 *  - Added Prototypes for InitSeed and RandomInteger
 *  - Changed return type of InitSeed from 'missing (default int)' to 'void'
 */

#define RPT 1

#include <math.h>

#define MAX 100

/* Statistics Program:
 * This program computes for two arrays of numbers the sum, the
 * mean, the variance, and standard deviation.  It then determines the
 * correlation coefficient between the two arrays.
 */

int Seed;
volatile double ArrayA[MAX], ArrayB[MAX];
volatile double SumA, SumB;
volatile double Coef;

/* Common understanding of a "small value" (epsilon) for floating point
   comparisons. */

#define VERIFY_DOUBLE_EPS 1.0e-7

#define double_eq_beebs(exp, actual) (fabs(exp - actual) < VERIFY_DOUBLE_EPS)

void InitSeed()
/*
 * Initializes the seed used in the random number generator.
 */
{
  Seed = 0;
}

void Calc_Sum_Mean(double Array[], double *Sum, double *Mean)
{
  int i;

  *Sum = 0;
  for (i = 0; i < MAX; i++)
    *Sum += Array[i];
  *Mean = *Sum / MAX;
}

int RandomInteger()
/*
 * Generates random integers between 0 and 8095
 */
{
  Seed = ((Seed * 133) + 81) % 8095;
  return (Seed);
}

double
Square(double x)
{
  return x * x;
}

void Calc_Var_Stddev(double Array[], double Mean, double *Var, double *Stddev)
{
  int i;
  double diffs;

  diffs = 0.0;
  for (i = 0; i < MAX; i++)
    diffs += Square(Array[i] - Mean);
  *Var = diffs / MAX;
  *Stddev = sqrt(*Var);
}

void Calc_LinCorrCoef(double ArrayA[], double ArrayB[], double MeanA,
                      double MeanB /*, Coef */)
{
  int i;
  double numerator, Aterm, Bterm;

  numerator = 0.0;
  Aterm = Bterm = 0.0;
  for (i = 0; i < MAX; i++)
  {
    numerator += (ArrayA[i] - MeanA) * (ArrayB[i] - MeanB);
    Aterm += Square(ArrayA[i] - MeanA);
    Bterm += Square(ArrayB[i] - MeanB);
  }

  /* Coef used globally */
  Coef = numerator / (sqrt(Aterm) * sqrt(Bterm));
}

void Initialize(double Array[])
/*
 * Intializes the given array with random integers.
 */
{
  register int i;

  for (i = 0; i < MAX; i++)
    Array[i] = i + RandomInteger() / 8095.0;
}

/* ------------------------------ main ------------------------------ */

int main()
{

    SysCtl_disableWatchdog();
    Device_init();
    Interrupt_initModule();
    Interrupt_initVectorTable();
    config_start_all_timers();

  volatile int correct, res, i;

  for (i = 0; i < RPT; i++)
  {
    volatile double MeanA, MeanB, VarA, VarB, StddevA, StddevB /*, Coef */;

    InitSeed();

    Initialize(ArrayA);
    Calc_Sum_Mean(ArrayA, &SumA, &MeanA);
    Calc_Var_Stddev(ArrayA, MeanA, &VarA, &StddevA);

    Initialize(ArrayB);
    Calc_Sum_Mean(ArrayB, &SumB, &MeanB);
    Calc_Var_Stddev(ArrayB, MeanB, &VarB, &StddevB);

    /* Coef will have to be used globally in Calc_LinCorrCoef since it would
       be beyond the 6 registers used for passing parameters
     */
    Calc_LinCorrCoef(ArrayA, ArrayB, MeanA, MeanB /*, &Coef */);
  }

  double expSumA = 4999.00247066090196;
  double expSumB = 4996.84311303273534;
  double expCoef = 0.999900054853619324;

  res = double_eq_beebs(expSumA, SumA) && double_eq_beebs(expSumB, SumB) && double_eq_beebs(expCoef, Coef);

  correct = res == 1 ? 1 : 0;

  StdOutInit(&ScibRegs, 921600, 12, GPIO_12_SCIB_TX);

  if (correct == 1) {
      puts("pass\n");
  } else {
      puts("fail\n");
  }

  EndSimulation();

  return 0;
}


